package com.xiaoma.weblog.admin.service.impl;

import com.google.common.collect.Lists;
import com.xiaoma.weblog.admin.model.vo.article.PublishArticleReqVO;
import com.xiaoma.weblog.admin.service.AdminArticleService;
import com.xiaoma.weblog.common.domain.dos.*;
import com.xiaoma.weblog.common.domain.mapper.*;
import com.xiaoma.weblog.common.enums.ResponseCodeEnum;
import com.xiaoma.weblog.common.exception.BizException;
import com.xiaoma.weblog.common.utils.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @ClassName AdminArticleServiceImpl
 * @Description:
 * @Author: 小马
 * @CreateDate: 2025/3/28 14:19
 * @Version: 1.0
 */
@Service
@Slf4j
public class AdminArticleServiceImpl implements AdminArticleService {
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private ArticleContentMapper articleContentMapper;
    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private TagMapper tagMapper;
    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;


    /**
     * 发布文章
     *
     * @param publishArticleReqVO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response publishArticle(PublishArticleReqVO publishArticleReqVO) {
        // 1. VO 转 ArticleDO, 并保存
        ArticleDO articleDO = ArticleDO.builder()
                .title(publishArticleReqVO.getTitle())
                .cover(publishArticleReqVO.getCover())
                .summary(publishArticleReqVO.getSummary())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        articleMapper.insert(articleDO);

        // 2. VO 转 ArticleContentDO，并保存
        ArticleContentDO articleContentDO = ArticleContentDO.builder()
                .articleId(articleDO.getId())
                .content(publishArticleReqVO.getContent())
                .build();
        articleContentMapper.insert(articleContentDO);

        // 3. 处理文章关联的分类
        Long categoryId = publishArticleReqVO.getCategoryId();
        // 3.1 校验提交的分类是否真实存在
        CategoryDO categoryDO = categoryMapper.selectById(categoryId);

        if (Objects.isNull(categoryDO)) {
            log.warn("==> 分类不存在, categoryId: {}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }


        ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder()
                .articleId(articleDO.getId())
                .categoryId(categoryId)
                .build();
        articleCategoryRelMapper.insert(articleCategoryRelDO);

        // 4. 保存文章关联的标签集合
        List<String> publishTags = publishArticleReqVO.getTags();
        insertTags(articleDO.getId(), publishTags);

        return Response.success();
    }

    /**
     * 保存标签
     *
     * @param articleId
     * @param publishTags
     */
    private void insertTags(Long articleId, List<String> publishTags) {
        // 筛选提交的标签（表中不存在的标签）
        List<String> notExistTags = null;
        // 筛选提交的标签（表中已存在的标签）
        List<String> existedTags = null;


        // 查询出所有标签
        List<TagDO> tagDOS = tagMapper.selectList(null);

        // 如果表中还没添加任何标签
        if (CollectionUtils.isEmpty(tagDOS)) {
            notExistTags = publishTags;
        } else {
            List<String> tagIds = tagDOS.stream().map(tagDO -> String.valueOf(tagDO.getId())).collect(Collectors.toList());
            existedTags = publishTags.stream().filter(tagIds::contains).collect(Collectors.toList());
            notExistTags = publishTags.stream().filter(publishTag -> !tagIds.contains(publishTag)).collect(Collectors.toList());
            // 补充逻辑：
            // 还有一种可能：按字符串名称提交上来的标签，也有可能是表中已存在的，比如表中已经有了 Java 标签，用户提交了个 java 小写的标签，需要内部装换为 Java 标签
            Map<String, Long> tagNameIdMap = tagDOS.stream().collect(Collectors.toMap(tagDO -> tagDO.getName().toLowerCase(), TagDO::getId));

            Iterator<String> iterator = notExistTags.iterator();
            while (iterator.hasNext()) {
                String notExistTag = iterator.next();
                if (tagNameIdMap.containsKey(notExistTag.toLowerCase())) {
                    iterator.remove();
                    existedTags.add(String.valueOf(tagNameIdMap.get(notExistTag.toLowerCase())));
                }
            }

            // 将提交的上来的，已存在于表中的标签，文章-标签关联关系入库
            if (!CollectionUtils.isEmpty(existedTags)) {
                List<ArticleTagRelDO> articleTagRelDOS = Lists.newArrayList();
                existedTags.forEach(tagId -> {
                    ArticleTagRelDO articleTagRelDO = ArticleTagRelDO.builder()
                            .articleId(articleId)
                            .tagId(Long.valueOf(tagId))
                            .build();
                    articleTagRelDOS.add(articleTagRelDO);
                });
                // 批量插入
                articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS);
            }

            // 将提交的上来的，不存在于表中的标签，入库保存
            if (!CollectionUtils.isEmpty(notExistTags)) {
                // 需要先将标签入库，拿到对应标签 ID 后，再把文章-标签关联关系入库
                List<ArticleTagRelDO> articleTagRelDOS = Lists.newArrayList();
                notExistTags.forEach(tagName -> {
                    TagDO tagDO = TagDO.builder()
                            .name(tagName)
                            .createTime(LocalDateTime.now())
                            .updateTime(LocalDateTime.now())
                            .build();

                    tagMapper.insert(tagDO);

                    // 拿到保存的标签 ID
                    Long tagId = tagDO.getId();

                    // 文章-标签关联关系
                    ArticleTagRelDO articleTagRelDO = ArticleTagRelDO.builder()
                            .articleId(articleId)
                            .tagId(tagId)
                            .build();
                    articleTagRelDOS.add(articleTagRelDO);
                });
                // 批量插入
                articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS);
            }
        }
    }
}